class Vec2 {
    values:Float32Array;
    constructor(x:number = 0,y:number = 0){
        this.values = new Float32Array([x,y])
    }

    get x(){
        return this.values[0]
    }
    set x(x:number){
        this.values[0] = x;
    }

    get y(){
        return this.values[1]
    }
    set y(y:number){
        this.values[1] = y;
    }

    reset(x:number = 0,y:number){
        this.values[0] = x;
        this.values[1] = y;
        return this
    }
    toString(){
        return `[${this.values[0].toFixed(2)} , ${this.values[1].toFixed(2)} ]`
    }
    static create(x:number = 0,y:number = 0){
        return new Vec2(x,y);
    }

    static copy(src: Vec2, result: Vec2 | null) {
        if (result == null) {
            result = new Vec2()
        }
        result.values[0] = src.values[0]
        result.values[1] = src.values[1]
        return result
    }

    equals(vector: Vec2) {
        if (Math.abs(this.values[0] - vector.values[0]) > EPSILON) {
            return false
        }
        if (Math.abs(this.values[1] - vector.values[1]) > EPSILON) {
            return false
        }
        return true;
    }

    // 向量大小(长度) 数值标量
    get squaredLength() {
        let x = this.values[0]
        let y = this.values[1]
        return x * x + y * y
    }
    get length() {
        return Math.sqrt(this.squaredLength)
    }
    // 方向 单位向量
    normalize() {
        let len = this.length;

        if (Math2D.isEquals(len, 0)) {
            this.values[0] = 0
            this.values[1] = 0
        }
        if (Math2D.isEquals(len, 1)) {
            return 1.0
        }
        // x / len 
        // y / len
        this.values[0] = this.values[0] / len;
        this.values[1] = this.values[1] / len

        return len
    }
    public static xAxis = new Vec2(1, 0);
    public static yAxis = new Vec2(0, 1);
    public static nXAxis = new Vec2(- 1, 0);
    public static nYAxis = new Vec2(0, - 1);
    // 负向向量
    negative() {
        this.values[0] = - this.values[0]
        this.values[1] = - this.values[1]
        return this
    }
    // 与标量相乘
    static scale(direction: Vec2, scalar: number, result: Vec2 | null = null) {
        if (result === null) {
            result = new Vec2()
        }
        result.values[0] = direction.values[0] * scalar
        result.values[1] = direction.values[1] * scalar
        return result
    }
    // 缩放相加 result = direction*scale+start
    static scaleAdd(
        start: Vec2, 
        direction: Vec2, 
        scalar: number, 
        result: Vec2 | null = null
    ) {
        if (result == null) {
            result = new Vec2()
        }
        Vec2.scale(direction, scalar, result)
        return Vec2.sum(start, result, result);
    }
    //2个向量加法
    static sum(left: Vec2, right: Vec2, result: Vec2 | null = null) {
        if (result == null) {
            result = new Vec2()
        }
        result.values[0] = left.values[0] + right.values[0]
        result.values[1] = left.values[1] + right.values[1]
        return result
    }
    // 加上某个向量
    add(right: Vec2) {
        Vec2.sum(this, right, this)
        return this
    }
    // 减法
    static difference(end: Vec2, start: Vec2, result: Vec2 | null = null) {
        if (result == null) {
            result = new Vec2()
        }
        result.values[0] = end.values[0] - start.values[0]
        result.values[1] = end.values[1] - start.values[1]
        return result
    }
    substract(another: Vec2) {
        Vec2.difference(this, another, this)
        return this
    }
    
    
    // 点乘
    static dotProduct(left: Vec2, right: Vec2) {
        return left.x * right.x + left.y * right.y
    }
    innerProduct(right: Vec2) {
        return Vec2.dotProduct(this, right)
    }
    // 使用点乘求向量夹角
    static getAngle(a: Vec2, b: Vec2, isRadian: boolean = false) {
        let dot = Vec2.dotProduct(a, b);
        let radian = Math.acos(dot / (a.length * b.length))
        return isRadian ? radian : Math2D.toDegree(radian)
    }
    // 求向量的角度
    static getOrientation(from: Vec2, to: Vec2, isRadian: boolean = false) {
        // 2个点的向量
        let diff = Vec2.difference(to, from);
        // 求x轴正方向的角度
        let radian = Math.atan2(diff.y, diff.x)
        return isRadian ? radian : Math2D.toDegree(radian)
    }

    static crossProduct(
        left: Vec2,
        right: Vec2
    ) {
        return left.x * right.y - left.y * right.x
    }

    static singAngle(
        a:Vec2,
        b:Vec2,
        norm:boolean = false
    ){
        if(norm === true){
            a.normalize();
            b.normalize()
        }
        return (a.x*b.y-b.x*a.y)
    }
    static cosAngle(
        a:Vec2,
        b:Vec2,
        norm:boolean = false
    ){
        if(norm === true){
            a.normalize();
            b.normalize()
        }
        return Vec2.dotProduct(a,b)
    }

    // 2个向量
    static sign(
        v0:Vec2,
        v1:Vec2,
        v2:Vec2
    ){
        let e1 = Vec2.difference(v0,v2)
        let e2 = Vec2.difference(v1,v2);
        return Vec2.crossProduct(e1,e2)
    }
}

// (orign,size) => (x,y,w,h)
class Rectangle {
    public origin: Vec2;
    public size: Size;

    public constructor(orign: Vec2 = new Vec2(), size: Size = new Size(1, 1)) {
        this.origin = orign;
        this.size = size;
    }

    public static create(x: number = 0, y: number = 0, w: number = 1, h: number = 1): Rectangle {
        let origin: Vec2 = new Vec2(x, y);
        let size: Size = new Size(w, h);
        return new Rectangle(origin, size);
    }
}
// w,h
class Size {
    public values: Float32Array;

    public constructor(w: number = 1, h: number = 1) {
        this.values = new Float32Array([w, h]);
    }

    set width(value: number) { this.values[0] = value; }
    get width(): number { return this.values[0]; }

    set height(value: number) { this.values[1] = value; }
    get height(): number { return this.values[1]; }

    public static create(w: number = 1, h: number = 1): Size {
        return new Size(w, h);
    }
}

// Math.PI / 180
const PiBy180: number = 0.017453292519943295;
const EPSILON: number = 0.00001;

class Math2D{
    // 角度转弧度
    static toRadian(degree:number){
        return degree * PiBy180
    }
    // 弧度转角度
    static toDegree(radian:number){
        return radian / PiBy180
    }
    static isEquals(
        left: number,
        right: number,
        esilon: number = EPSILON
    ) {
        if (Math.abs(left - right) >= EPSILON) {
            return false
        }
        return true
    }
    /**
     * 求点到线段的投影
     * @param pt    目标点
     * @param start 线段起点 
     * @param end   线段结束点
     * @param closePoint  投影点
     */
    static projectPointOnLineSegment(
        pt:Vec2,
        start:Vec2,
        end:Vec2,
        closePoint:Vec2
    ){
        // start->pt
        let v0 = Vec2.create();
        // start->end
        let v1 = Vec2.create();
        let d = 0;

        Vec2.difference(pt,start,v0)
        Vec2.difference(end,start,v1)
        d = v1.normalize();

        let t = Vec2.dotProduct(v0,v1);

        if(t < 0){
            closePoint.x = start.x;
            closePoint.y = start.y;
            return false;
        } else if(t > d){
            closePoint.x = end.x;
            closePoint.y = end.y;
            return false
        }else {
            Vec2.scaleAdd(start,v1,t,closePoint)
            return true;
        }
    }

    static isPointInCircle(
        pt:Vec2,
        center:Vec2,
        radius : number
    ){
        let diff = Vec2.difference(pt,center);
        let len = diff.squaredLength;

        if(len <= radius * radius){
            return true;
        }
        return false
    }

    static isPointOnLineSegment(
        pt:Vec2,
        start:Vec2,
        end:Vec2,
        radius = 2
    ){
        let closePt = Vec2.create();
        if(Math2D.projectPointOnLineSegment(
            pt,start,end,closePt
        )){
            return Math2D.isPointInCircle(
                pt,closePt,radius
            )
        }
        return false;
    }

    static isPointInRect(
        ptX:number,
        ptY:number,
        x:number,
        y:number,
        w:number,
        h:number
    ){
        return ptX >= x && ptX <= x + w
            && ptY >= y && ptY <= y + h
    }

    static isPointInEllipse(
        ptX: number,
        ptY: number,
        centerX: number,
        centerY: number,
        radiusX: number,
        radiusY: number
    ) {
        let diffX = ptX - centerX
        let diffY = ptY - centerY
        let n = (diffX * diffY) / (radiusX * radiusX)
            + (diffY * diffY) / (radiusY * radiusY)
        return (n <= 1.0)
    }

    static isPointInTriangle(
        pt:Vec2,
        v0:Vec2,
        v1:Vec2,
        v2:Vec2
    ){
        let b1 = Vec2.sign(v0,v1,pt) < 0.0;
        let b2 = Vec2.sign(v1,v2,pt) < 0.0;
        let b3 = Vec2.sign(v2,v0,pt) < 0.0;
        
        return (b1 === b2) && (b2 === b3)
    }

    static isPointInPolygon(
        pt: Vec2,
        points: Vec2[]
    ) {
        if (points.length < 3) {
            return false
        }

        for (let i = 2; i < points.length; i++) {
            if (Math2D.isPointInTriangle(
                pt,
                points[0],
                points[i - 1],
                points[i]
            )) {
                return true;
            }
        }
        return false
    }

    static isConvex(
        points: Vec2[]
    ) {
        let sign = Vec2.sign(points[0], points[1], points[2]) < 0
        let j: number, k: number;
        for (let i = 1; i < points.length; i++) {
            j = (i + 1) % points.length
            k = (i + 2) % points.length
            if (sign !== (Vec2.sign(
                points[i],
                points[j],
                points[k]
            ) < 0)) {
                return false
            }
        }
        return true
    }
}

export {
    Vec2,
    Rectangle,
    Size,
    Math2D
}